// sgu327
// 题意：
// 给定n(<=14)个串，每个串长度不超过30。要求找到一个长度最小的回文串，
// 使得每个输入的串都是这个串的子串。
//
// 题解：
// 状压。一开始想错了，想着枚举原串在目标串的左侧还是右侧，其实没必要，
// 因为就算枚举了之后也没有复杂度很低的方法(这很像之前某到dp?)。
//
// 那么其实我们先去除一些被包含的串，然后设f[state][last]表示
// 当前选取的字符串状态集合为state，last是最后一个串，并且对于一个串
// 要考虑正着取和反着取两种情况，得到的拼接起来的最小长度。
// 我们这么做可以得到一侧的最短长度比如右边。然后把这个串反过来拼接到左边。
//
// 因为回文串中间可能正好被某个串覆盖，所以我们初始可以枚举这个这个串
// 作为起始状态。复杂度是O(n^2 * 2^n)。
//
// 一开始记忆化的时候直接存的字符串，但是sgu时间给的很少，TLE了，所以改成
// 存长度，做两次记忆化。
//
// run: time -p $exec < input
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>

int const maxn = 33;
int overlap[maxn][maxn];
std::vector<std::string> s;
std::vector<std::string> all;
int n;

std::string reverse(std::string const& s)
{
	auto ret = s;
	std::reverse(ret.begin(), ret.end());
	return ret;
}

bool palindrome(std::string const& s)
{
	return reverse(s) == s;
}

bool contain(std::string const& a, std::string const& b)
{
	return a.find(b) != std::string::npos || a.find(reverse(b)) != std::string::npos;
}

int f[1 << 15][maxn];

int dp(int state, int last)
{
	int& len = f[state][last];
	if (len >= 0) return len;
	if (state + 1 == (1 << n)) return len = 0;
	len = maxn * 30;
	for (int i = 0; i < 2 * n; i++)
		if ((1 << (i / 2)) & (~state))
			len = std::min(len, (int)all[i].size() - overlap[last][i] + dp(state | (1 << (i / 2)), i));
	return len;
}

std::string construct(int state, int last)
{
	if (state + 1 == (1 << n)) return "";
	int len = dp(state, last);
	for (int i = 0; i < 2 * n; i++)
		if (((1 << (i / 2)) & (~state)) && len == (int)(all[i].size() - overlap[last][i] + dp(state | (1 << (i / 2)), i)))
			return all[i].substr(overlap[last][i]) + construct(state | (1 << (i / 2)), i);
	return "failed!";
}

int main()
{
	std::ios::sync_with_stdio(false);
	std::cin >> n;
	s.resize(n);
	// remove contained string
	for (int i = 0; i < n; i++) std::cin >> s[i];
	for (int i = 0; i < (int)s.size(); i++)
		for (int j = i + 1; j < (int)s.size(); j++)
			if (contain(s[i], s[j])) s.erase(s.begin() + j--);
	std::reverse(s.begin(), s.end());
	for (int i = 0; i < (int)s.size(); i++)
		for (int j = i + 1; j < (int)s.size(); j++)
			if (contain(s[i], s[j])) s.erase(s.begin() + j--);

	// all string state, reversed or not
	n = s.size();
	all.resize(2 * n);
	for (int i = 0; i < 2 * n; i += 2) {
		all[i] = s[i / 2];
		all[i + 1] = reverse(all[i]);
	}

	// init overlap
	for (int i = 0; i < 2 * n; i++)
		for (int j = 0; j < 2 * n; j++) {
			for (int l = 1; l <= (int)std::min(all[i].size(), all[j].size()); l++)
				if (all[i].substr(all[i].size() - l) == all[j].substr(0, l))
					overlap[i][j] = l;
		}

	// main compressed dp
	std::memset(f, -1, sizeof(f));
	std::string ans(maxn * 30, 'a');
	for (int i = 0; i < 2 * n; i++) {
		auto r = all[i] + construct(1 << (i / 2), i);
		if (r.size() > ans.size()) continue;
		auto l = reverse(r);
		do {
			auto tmp = l + r;
			if (palindrome(tmp) && ans.size() > tmp.size()) ans = tmp;
			if (l.empty()) break;
			l.pop_back();
		} while (true);
	}
	std::cout << ans << "\n";
}

